package website

import (
	"encoding/json"
	"strings"
	"utils"

	"bufio"

	"io"

	"time"

	gossh "golang.org/x/crypto/ssh"

	iris "gopkg.in/kataras/iris.v6"
	"gopkg.in/kataras/iris.v6/adaptors/websocket"
)

//Index *
type Index struct{}

//Main *
func (i *Index) Main(ctx *iris.Context) {
	sessionUser := ctx.Session().Get("user_info")
	if nil != sessionUser {
		ctx.Render("index.html", sessionUser)
	} else {
		ctx.Redirect("/login")
	}
}

var wsRoom string

func sshHandle(c websocket.Connection) {
	LogDebugln("Connection:", c.ID())

	c.Join(wsRoom)

	wsStop := func(wsConn websocket.Connection, sshSession *gossh.Session) {
		wsConn.On("stop", func(msg string) {
			msg = strings.ToLower(msg)
			LogDebugln("STOP:", msg)
			if "stop" == strings.TrimSpace(msg) {
				if err := sshSession.Close(); nil != err {
					newWSResult(wsConn, "error", false, "no", "\n"+err.Error())
				}
			}
		})
	}

	c.OnDisconnect(func() {
		newWSResult(c, "error", false, "no", "\nError: Client disconnect.")
	})

	c.On("start", func(s string) {
		var wsParam WsParam
		if err := json.Unmarshal([]byte(s), &wsParam); nil != err {
			newWSResult(c, "error", false, "no", err.Error())
		}

		b64Cmd, err := utils.StringUtils(wsParam.Cmd).Base64Decode()
		if nil != err {
			newWSResult(c, "error", false, "no", err.Error())
		}

		b64Vars, err := utils.StringUtils(wsParam.Vars).Base64Decode()
		if nil != err {
			newWSResult(c, "error", false, "no", err.Error())
		}

		LogDebugln("b64Vars:", b64Vars)

		sessionUser := c.Context().Session().Get("user_info")

		if nil != sessionUser {
			u := sessionUser.(User)

			s, err := NewSSH(u.User, u.Pwd, u.Addr)
			if nil != err {
				newWSResult(c, "error", false, "no", err.Error())
			}
			ssh, err := s.Connect()
			if nil != err {
				newWSResult(c, "error", false, "no", err.Error())
			}

			sshSession, err := ssh.Client.NewSession()
			if nil != err {
				newWSResult(c, "error", false, "no", err.Error())
			}
			defer sshSession.Close()

			wsStop(c, sshSession)

			var varList []SSHVar

			if err := json.Unmarshal([]byte(b64Vars), &varList); nil != err {
				newWSResult(c, "error", false, "no", err.Error())
			}

			LogDebugln("varList:", varList)

			for _, v := range varList {
				name := strings.TrimSpace(v.Name)
				value := strings.TrimSpace(v.Val)
				if "" != name {
					sshSession.Setenv(name, value)
				}
			}

			stderr, err := sshSession.StderrPipe()
			if nil != err {
				newWSResult(c, "error", false, "no", err.Error())
			}
			go wsPipe(stderr, c)

			stdout, err := sshSession.StdoutPipe()
			if nil != err {
				newWSResult(c, "error", false, "no", err.Error())
			}
			go wsPipe(stdout, c)

			if err := sshSession.Run(b64Cmd); nil != err {
				newWSResult(c, "error", false, "no", err.Error())
			}
		} else {
			newWSResult(c, "error", false, "no", "Invalid user.")
		}
	})
}

func newWSResult(c websocket.Connection, event string, ok bool, msg string, data interface{}) {
	if err := c.To(wsRoom).Emit(event, Result{
		Ok:   ok,
		Msg:  strings.TrimSpace(msg),
		Data: data,
	}); nil != err {
		LogErrorln(err)
	}
}

func wsPipe(writer io.Reader, c websocket.Connection) {
	stdoutReader := bufio.NewReaderSize(writer, 1024)
	buf := make([]byte, 1024)
	for {
		n, err := stdoutReader.Read(buf)
		if io.EOF == err {
			break
		}

		if nil != err {
			newWSResult(c, "error", false, "no", err.Error())
			return
		}

		if n == 0 {
			break
		}

		wsStrData := string(buf[:n])
		newWSResult(c, "ssh", true, "ok", wsStrData)
		time.Sleep(time.Second * 2)
	}
}

func init() {
	index := &Index{}
	Get("/", index.Main)
}
